package com.mlethe.library.widget.vertical.textview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 竖直文字显示View
 *
 * @author Mlethe
 * @date 2020/9/9
 */
public class VerticalTextView extends View {

    private static final int TOP = 1;
    private static final int BOTTOM = 2;
    private static final int CENTER = 3;

    @IntDef({TOP, BOTTOM, CENTER})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Gravity {
    }

    private Map<Integer, Integer> mHashMap = new HashMap<>();

    private Paint mPaint;

    private float mWidthSize, mHeightSize;

    /**
     * 文字列数
     */
    private int mColumn;
    /**
     * 每列文字数量
     */
    private int mColumnSize;
    /**
     * 文字是否测量，true 已测量，false 未测量
     */
    private boolean isMeasure;
    /**
     * 单个文字最大宽度、高度
     */
    private float mMaxTextWidth, mMaxTextHeight;
    /**
     * 绘制的文本
     */
    private CharSequence mCharSequence;
    /**
     * 文字大小
     */
    private float mTextSize;
    /**
     * 文字颜色
     */
    private int mTextColor;
    /**
     * 对齐方式
     */
    private int mGravity;
    /**
     * 字体样式
     */
    private int mTextStyle;
    /**
     * 列间距(行间距)
     */
    private float mLineSpacing;
    /**
     * 字间距
     */
    private float mLetterSpacing;

    public VerticalTextView(Context context) {
        this(context, null);
    }

    public VerticalTextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VerticalTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalTextView);
            mWidthSize = a.getLayoutDimension(R.styleable.VerticalTextView_android_layout_width, 0);
            mHeightSize = a.getLayoutDimension(R.styleable.VerticalTextView_android_layout_height, 0);
            mCharSequence = a.getText(R.styleable.VerticalTextView_android_text);
            mTextSize = a.getDimension(R.styleable.VerticalTextView_android_textSize, sp2px(14));
            mTextColor = a.getColor(R.styleable.VerticalTextView_android_textColor, Color.GRAY);
            mGravity = a.getInt(R.styleable.VerticalTextView_gravity, TOP);
            mTextStyle = a.getInt(R.styleable.VerticalTextView_android_textStyle, 0);
            mLineSpacing = a.getDimension(R.styleable.VerticalTextView_android_lineSpacingExtra, dp2px(2));
            mLetterSpacing = a.getDimension(R.styleable.VerticalTextView_letterSpacingExtra, 0);
            a.recycle();
        }
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @Override
    public void requestLayout() {
        isMeasure = false;
        super.requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (mWidthSize == 0 || mHeightSize == 0 || mCharSequence == null || mCharSequence.length() <= 0) {
            if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(getPaddingLeft() + getPaddingRight(), MeasureSpec.EXACTLY);
            }
            if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY);
            }
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        if (!isMeasure) {
            isMeasure = true;
            if (mTextStyle == 0) {
                mPaint.setTypeface(Typeface.DEFAULT);
            } else if (mTextStyle == 1) {
                mPaint.setTypeface(Typeface.DEFAULT_BOLD);
            } else if (mTextStyle == 2) {
                Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.ITALIC);
                mPaint.setTypeface(font);
            } else {
                mPaint.setTypeface(Typeface.DEFAULT);
            }
            measureText();
        }
        int length = mCharSequence.length();
        float totalTextHeight = mMaxTextHeight * length + mLetterSpacing * (length - 1);
        if (heightMode == MeasureSpec.UNSPECIFIED) {
            mColumn = 1;
            mColumnSize = length;
            float heightSize = totalTextHeight + getPaddingTop() + getPaddingBottom();
            heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) Math.ceil(heightSize), MeasureSpec.EXACTLY);
            float widthSize = mMaxTextWidth * mColumn + getPaddingLeft() + getPaddingRight();
            int widthMeasureSize = MeasureSpec.getSize(widthMeasureSpec);
            if (widthMode == MeasureSpec.EXACTLY) {
                widthSize = widthMeasureSize;
            } else if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
                if (widthSize > widthMeasureSize) {
                    widthSize = widthMeasureSize;
                }
            }
            widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) widthSize, MeasureSpec.EXACTLY);
        } else if (mHeightSize == ViewGroup.LayoutParams.MATCH_PARENT) {
            float heightSize = MeasureSpec.getSize(heightMeasureSpec);
            float realHeight = heightSize - getPaddingTop() - getPaddingBottom();
            mColumnSize = (int) ((realHeight + mLetterSpacing) / (mMaxTextHeight + mLetterSpacing));
            mColumn = (int) (totalTextHeight / realHeight);
            if (totalTextHeight % realHeight > 0) {
                mColumn++;
            }
            float widthSize = mMaxTextWidth * mColumn + mLineSpacing * (mColumn - 1) + getPaddingLeft() + getPaddingRight();
            int widthMeasureSize = MeasureSpec.getSize(widthMeasureSpec);
            if (widthMode == MeasureSpec.EXACTLY) {
                widthSize = widthMeasureSize;
            } else if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
                if (widthSize > widthMeasureSize) {
                    widthSize = widthMeasureSize;
                }
            }
            widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) widthSize, MeasureSpec.EXACTLY);
        } else if (mHeightSize == ViewGroup.LayoutParams.WRAP_CONTENT) {
            float heightSize = MeasureSpec.getSize(heightMeasureSpec);
            mColumn = 1;
            if (totalTextHeight > heightSize) {
                float realHeight = heightSize - getPaddingTop() - getPaddingBottom();
                mColumnSize = (int) ((realHeight + mLetterSpacing) / (mMaxTextHeight + mLetterSpacing));
                mColumn = (int) (totalTextHeight / realHeight);
                if (totalTextHeight % realHeight > 0) {
                    mColumn++;
                }
            } else {
                mColumnSize = length;
                heightSize = totalTextHeight;
            }
            float widthSize = mMaxTextWidth * mColumn + mLineSpacing * (mColumn - 1) + getPaddingLeft() + getPaddingRight();
            int widthMeasureSize = MeasureSpec.getSize(widthMeasureSpec);
            if (widthMode == MeasureSpec.EXACTLY) {
                widthSize = widthMeasureSize;
            } else if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
                if (widthSize > widthMeasureSize) {
                    widthSize = widthMeasureSize;
                }
            }
            widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) widthSize, MeasureSpec.EXACTLY);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) heightSize, MeasureSpec.EXACTLY);
        } else {
            float heightSize = mHeightSize;
            float realHeight = heightSize - getPaddingTop() - getPaddingBottom();
            mColumnSize = (int) ((realHeight + mLetterSpacing) / (mMaxTextHeight + mLetterSpacing));
            mColumn = (int) (totalTextHeight / realHeight);
            if (totalTextHeight % realHeight > 0) {
                mColumn++;
            }
            float widthSize = mMaxTextWidth * mColumn + mLineSpacing * (mColumn - 1) + getPaddingLeft() + getPaddingRight();
            int widthMeasureSize = MeasureSpec.getSize(widthMeasureSpec);
            if (widthMode == MeasureSpec.EXACTLY) {
                widthSize = widthMeasureSize;
            } else if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
                if (widthSize > widthMeasureSize) {
                    widthSize = widthMeasureSize;
                }
            }
            widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) widthSize, MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        if (mCharSequence == null || mCharSequence.length() <= 0) {
            return;
        }
        int height = getHeight();
        int length = mCharSequence.length();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        float startY = paddingTop;
        if (mGravity == BOTTOM) {
            startY = height - (mMaxTextHeight * mColumnSize + mLetterSpacing * (mColumnSize - 1));
        } else if (mGravity == CENTER) {
            startY = (height - (mMaxTextHeight * mColumnSize + mLetterSpacing * (mColumnSize - 1))) / 2;
        }
        float x = getPaddingLeft();
        float y = startY;
        int baseLine = getBaseLine();
        int currentColumn = 1;
        for (int i = 0; i < length; i++) {
            String s = String.valueOf(mCharSequence.charAt(i));
            float offsetX = (mMaxTextWidth - getTextWidth(s)) / 2;
            if (y + mMaxTextHeight + mLetterSpacing > height - paddingBottom) {
                // 换列(换行)
                x += mMaxTextWidth + mLineSpacing;
                currentColumn++;
                if (currentColumn == mColumnSize) {
                    // 最后一列
                    // 剩余字数
                    int remainSize = length - i;
                    if (mGravity == BOTTOM) {
                        startY = height - (mMaxTextHeight * remainSize + mLetterSpacing * (remainSize - 1));
                    } else if (mGravity == CENTER) {
                        startY = (height - (mMaxTextHeight * remainSize + mLetterSpacing * (remainSize - 1))) / 2;
                    }
                }
                y = startY;
            }
            if (y != startY) {
                y += mLetterSpacing;
                y += mMaxTextHeight;
            } else {
                y += baseLine;
            }
            if (mHashMap.containsKey(i)) {
                Integer color = mHashMap.get(i);
                if (color != null) {
                    mPaint.setColor(color);
                } else {
                    mPaint.setColor(mTextColor);
                }
            } else {
                mPaint.setColor(mTextColor);
            }
            canvas.drawText(mCharSequence, i, i + 1, x + offsetX, y, mPaint);
        }
    }

    /**
     * 测量文字
     */
    private void measureText() {
        mMaxTextWidth = 0;
        if (mCharSequence == null || mCharSequence.length() <= 0) {
            return;
        }
        mPaint.setTextSize(mTextSize);
        mMaxTextHeight = getTextHeight();
        for (int i = 0; i < mCharSequence.length(); i++) {
            String s = String.valueOf(mCharSequence.charAt(i));
            mMaxTextWidth = Math.max(mMaxTextWidth, getTextWidth(s));
        }
    }

    /**
     * 基线位置
     *
     * @return
     */
    private int getBaseLine() {
        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        return -fontMetrics.ascent;
    }

    /**
     * 获取文字宽度
     *
     * @param str
     * @return
     */
    private float getTextWidth(String str) {
        return mPaint.measureText(str);
    }

    /**
     * 获取文字高度
     *
     * @return
     */
    private float getTextHeight() {
        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        return fontMetrics.descent - fontMetrics.ascent;
    }

    /**
     * sp转px
     *
     * @param sp
     * @return
     */
    private float sp2px(float sp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

    /**
     * dp 转 px
     *
     * @param dp
     */
    protected float dp2px(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }

    /**
     * 设置文本
     *
     * @param charSequence
     * @return
     */
    public VerticalTextView setText(CharSequence charSequence) {
        this.mCharSequence = charSequence;
        requestLayout();
        return this;
    }

    /**
     * 设置文本
     *
     * @param resId
     * @return
     */
    public VerticalTextView setText(@StringRes int resId) {
        return setText(getResources().getString(resId));
    }

    /**
     * 设置文本
     *
     * @param value
     * @return
     */
    public VerticalTextView setTextInt(int value) {
        return setText(String.valueOf(value));
    }

    /**
     * 设置文本
     *
     * @param value
     * @return
     */
    public VerticalTextView setText(float value) {
        return setText(String.valueOf(value));
    }

    /**
     * 设置文本
     *
     * @param value
     * @return
     */
    public VerticalTextView setText(double value) {
        return setText(String.valueOf(value));
    }

    /**
     * 设置文本
     *
     * @param value
     * @return
     */
    public VerticalTextView setText(boolean value) {
        return setText(String.valueOf(value));
    }

    /**
     * 设置文本
     *
     * @param value
     * @return
     */
    public VerticalTextView setText(long value) {
        return setText(String.valueOf(value));
    }

    /**
     * 设置文本
     *
     * @param value
     * @return
     */
    public VerticalTextView setText(char value) {
        return setText(String.valueOf(value));
    }

    /**
     * 设置文本
     *
     * @param value
     * @return
     */
    public VerticalTextView setText(short value) {
        return setText(String.valueOf(value));
    }

    /**
     * 设置文本大小
     *
     * @param textSize sp
     * @return
     */
    public VerticalTextView setTextSize(float textSize) {
        this.mTextSize = sp2px(textSize);
        requestLayout();
        return this;
    }

    /**
     * 设置文本颜色
     *
     * @param color
     * @return
     */
    public VerticalTextView setTextColorInt(@ColorInt int color) {
        this.mTextColor = color;
        invalidate();
        return this;
    }

    /**
     * 设置文本颜色
     *
     * @param colorRes
     * @return
     */
    public VerticalTextView setTextColor(@ColorRes int colorRes) {
        return setTextColorInt(ContextCompat.getColor(getContext(), colorRes));
    }

    /**
     * 设置文本颜色
     *
     * @param colorString
     * @return
     */
    public VerticalTextView setTextColor(String colorString) {
        return setTextColorInt(Color.parseColor(colorString));
    }

    /**
     * 设置文本对齐方式
     *
     * @param gravity
     * @return
     */
    public VerticalTextView setGravity(@Gravity int gravity) {
        this.mGravity = gravity;
        invalidate();
        return this;
    }

    /**
     * 设置列间距(行间距)
     *
     * @param lineSpacing px
     * @return
     */
    public VerticalTextView setLineSpacing(int lineSpacing) {
        this.mLineSpacing = lineSpacing;
        requestLayout();
        return this;
    }

    /**
     * 设置列间距(行间距)
     *
     * @param lineSpacing px
     * @return
     */
    public VerticalTextView setLineSpacing(float lineSpacing) {
        return setLineSpacing((int) dp2px(lineSpacing));
    }

    /**
     * 设置字间距
     *
     * @param letterSpacing px
     * @return
     */
    public VerticalTextView setLetterSpacing(int letterSpacing) {
        this.mLetterSpacing = letterSpacing;
        requestLayout();
        return this;
    }

    /**
     * 设置字间距
     *
     * @param letterSpacing dp
     * @return
     */
    public VerticalTextView setLetterSpacing(float letterSpacing) {
        return setLetterSpacing((int) dp2px(letterSpacing));
    }

    /**
     * 设置文本的不同颜色
     *
     * @param color 颜色
     * @param list  该颜色文字的index
     * @return
     */
    public VerticalTextView addOtherColorInt(@ColorInt int color, List<Integer> list) {
        if (list == null || list.size() <= 0) {
            return this;
        }
        for (Integer index : list) {
            mHashMap.put(index, color);
        }
        invalidate();
        return this;
    }

    /**
     * 设置文本的不同颜色
     *
     * @param colorRes 颜色资源
     * @param list     该颜色文字的index
     * @return
     */
    public VerticalTextView addOtherColor(@ColorRes int colorRes, List<Integer> list) {
        return addOtherColorInt(ContextCompat.getColor(getContext(), colorRes), list);
    }

    /**
     * 设置文本的不同颜色
     *
     * @param colorString 颜色
     * @param list        该颜色文字的index
     * @return
     */
    public VerticalTextView addOtherColor(String colorString, List<Integer> list) {
        return addOtherColorInt(Color.parseColor(colorString), list);
    }
}
